<?php

namespace Xrfg\Wallet\ETH\Services;

use Exception;
use GuzzleHttp\Client;
use Illuminate\Support\Facades\Log;
use phpseclib\Math\BigInteger;
use App\Models\Withdraw;
use App\Models\User;
use Xrfg\Wallet\ETH\Traits\ETHHelper;

/**
 * @method string eth_blockNumber()
 * @method array  eth_getBlockByNumber(string $blockNumber, bool $true)
 * @method array  eth_getLogs(array $options)
 * @method        personal_sendTransaction(array $array, string $password)
 */
class ETHWallet
{
    use ETHHelper;

    /**
     * @var \GuzzleHttp\Client
     */
    public $http;

    public $config;

    /**
     * ETHService constructor.
     *
     * @param array $config
     */
    public function __construct(array $config)
    {
        $this->http = new Client();
        $this->config = $config;
    }

    /**
     * @param User $user
     *
     * @return mixed
     *
     * @throws Exception
     */
    public function newAccount(User $user)
    {
        return self::request('personal_newAccount', [config('wallet.eth.password')]);
    }

    public function transferETH(Withdraw $withdraw)
    {
        $from = config('wallet.eth.withdraw_address');
        $to = $withdraw->address;
        $gas = $this->num2hex(21000);
        $gasPrice = '0x' . ltrim((new BigInteger(10 * 10 ** 9, 10))->toHex(), '0');
        $value = $this->ether2wei($withdraw->number());

        try {
            $hash = $this->personal_sendTransaction(
                compact('from', 'to', 'gas', 'gasPrice', 'value'),
                config('wallet.eth.password')
            );
            $withdraw->hash = $hash;
            $withdraw->status = Withdraw::STATUS_SUCCESS;
            $withdraw->save();
        } catch (Exception $exception) {
            Log::error($exception);
        }
    }

    public function transferERC20(Withdraw $withdraw)
    {
        $config = collect(config('wallet.eth.contracts'))
            ->where('id', $withdraw->coin_id)
            ->toArray();

        $from = config('wallet.eth.withdraw_address');
        $to = array_key_first($config);
        $gas = $this->num2hex(new BigInteger(60000));
        //$gasPrice = '0x'.ltrim((new BigInteger(10 * 10 ** 9, 40))->toHex(), '0');
        $gasPrice = $this->num2hex(new BigInteger(30 * pow(10, 9)));
        $value = '0x0';
        $data = '0x';
        $data .= str_pad('a9059cbb', 32, '0', STR_PAD_RIGHT);
        $data .= str_pad(mb_substr($withdraw->address, 2), 40, '0', STR_PAD_RIGHT);
        $data .= str_pad(substr($this->ether2wei($withdraw->number(), array_first($config)['decimals']), 2), 64, '0', STR_PAD_LEFT);

        try {
            $hash = $this->personal_sendTransaction(
                compact('from', 'to', 'gas', 'gasPrice', 'value', 'data'),
                config('wallet.eth.password')
            );
            $withdraw->hash = $hash;
            $withdraw->status = Withdraw::STATUS_SUCCESS;
            $withdraw->save();
        } catch (Exception $exception) {
            Log::error($exception);
        }
    }

    /**
     * @param $method
     * @param array $params
     *
     * @return \Psr\Http\Message\ResponseInterface
     *
     * @throws Exception
     */
    public function request($method, $params = [])
    {
        $response = $this->http->post($this->config['base_uri'], [
            'timeout' => $this->config['timeout'],
            'json' => [
                'id' => time(),
                'jsonrpc' => '2.0',
                'method' => $method,
                'params' => $params,
            ],
        ]);

        $content = json_decode($response->getBody()->getContents(), true);

        if (isset($content['error'])) {
            Log::error(json_encode($content['error']));
            throw new Exception('RPC ERROR');
        }

        return $content['result'];
    }

    /**
     * @param $name
     * @param $arguments
     *
     * @return \Psr\Http\Message\ResponseInterface
     *
     * @throws Exception
     */
    public function __call($name, $arguments)
    {
        return self::request($name, $arguments);
    }
}
